package com.code.ape.codeape.common.security.component;

import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import com.code.ape.codeape.common.core.config.HttpRequestWrapper;
import com.code.ape.codeape.common.core.constant.CacheConstants;
import com.code.ape.codeape.common.core.util.AssertUtil;
import com.code.ape.codeape.common.core.util.MD5Util;
import com.code.ape.codeape.common.security.annotation.RepeatSubmit;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.text.MessageFormat;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 公众号：码猿技术专栏
 * @url: www.java-family.cn
 * @description 防重复点击注解切面
 */
@Slf4j
@RequiredArgsConstructor
@Aspect
public class CodeapeRepeatSubmitAspect implements Ordered {

	private final StringRedisTemplate stringRedisTemplate;

	@SneakyThrows
	@Around("@within(repeatSubmit) || @annotation(repeatSubmit)")
	public Object around(ProceedingJoinPoint joinPoint, RepeatSubmit repeatSubmit) {
		HttpServletRequest request = ((ServletRequestAttributes) Objects
				.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

		//请求的URL
		String requestURI = URLUtil.getPath(request.getRequestURI());
		//请求的IP地址
		String clientIP = ServletUtil.getClientIP(request);
		//请求的参数
		String params = getParams(request);

		//获取参数
		String key= MessageFormat.format(CacheConstants.REPEAT_SUBMIT_KEY,requestURI,Math.abs(clientIP.hashCode()),Math.abs(MD5Util.encrypt(params).hashCode()));
		//存在即返回false，不存在即返回true
		Boolean ifAbsent = stringRedisTemplate.opsForValue().setIfAbsent(key, "", repeatSubmit.seconds(), TimeUnit.SECONDS);
		AssertUtil.isTrue(ifAbsent,"请勿重复提交！！！");
		return joinPoint.proceed();
	}


	@Override
	public int getOrder() {
		return Ordered.HIGHEST_PRECEDENCE + 3;
	}

	private String getParams(HttpServletRequest request) throws Exception {
		String params;
		if (request instanceof HttpRequestWrapper){
			params = request.getReader().readLine();
		}else {
			params= HttpUtil.toParams(request.getParameterMap());
		}
		return params;
	}
}
